 /*******************************************************************************
  * Copyright (c) 2003, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.osgi.framework.internal.core;

 import java.io.IOException ;
 import java.net.URL ;
 import java.util.ArrayList ;
 import java.util.Enumeration ;
 import org.eclipse.osgi.framework.util.KeyedHashSet;
 import org.eclipse.osgi.service.resolver.*;
 import org.osgi.framework.*;
 import org.osgi.service.packageadmin.RequiredBundle;

 /*
  * The BundleLoaderProxy proxies a BundleLoader object for a Bundle. This
  * allows for a Bundle's depedencies to be linked without forcing the
  * creating of the BundleLoader or BundleClassLoader objects. This class
  * keeps track of the depedencies between the bundles installed in the
  * Framework.
  */
 public class BundleLoaderProxy implements RequiredBundle {
     // The BundleLoader that this BundleLoaderProxy is managing
 private BundleLoader loader;
     // The Bundle that this BundleLoaderProxy is for
 final private BundleHost bundle;
     // the BundleDescription for the Bundle
 final private BundleDescription description;
     // Indicates if this BundleLoaderProxy is stale;
 // this is true when the bundle is updated or uninstalled.
 private boolean stale = false;
     // cached of package sources for the bundle
 final private KeyedHashSet pkgSources;

     public BundleLoaderProxy(BundleHost bundle, BundleDescription description) {
         this.bundle = bundle;
         this.description = description;
         this.pkgSources = new KeyedHashSet(false);
     }

     synchronized BundleLoader getBundleLoader() {
         if (loader != null)
             return loader;
         if (bundle.isResolved()) {
             try {
                 if (bundle.getBundleId() == 0) // this is the system bundle
 loader = new SystemBundleLoader(bundle, this);
                 else
                     loader = new BundleLoader(bundle, this);
             } catch (BundleException e) {
                 bundle.framework.publishFrameworkEvent(FrameworkEvent.ERROR, bundle, e);
                 return null;
             }
         }
         return loader;
     }

     BundleLoader getBasicBundleLoader() {
         return loader;
     }

     AbstractBundle getBundleHost() {
         return bundle;
     }

     void setStale() {
         stale = true;
     }

     boolean isStale() {
         return stale;
     }

     public String toString() {
         String symbolicName = bundle.getSymbolicName();
         StringBuffer sb = new StringBuffer (symbolicName == null ? bundle.getBundleData().getLocation() : symbolicName);
         sb.append("; ").append(Constants.BUNDLE_VERSION_ATTRIBUTE); //$NON-NLS-1$
 sb.append("=\"").append(description.getVersion().toString()).append("\""); //$NON-NLS-1$//$NON-NLS-2$
 return sb.toString();
     }

     public org.osgi.framework.Bundle getBundle() {
         if (isStale())
             return null;

         return bundle;
     }

     public org.osgi.framework.Bundle[] getRequiringBundles() {
         if (isStale())
             return null;
         // This is VERY slow; but never gets called in regular execution.
 BundleDescription[] dependents = description.getDependents();
         if (dependents == null || dependents.length == 0)
             return null;
         ArrayList result = new ArrayList (dependents.length);
         for (int i = 0; i < dependents.length; i++)
             addRequirers(dependents[i], result);
         return result.size() == 0 ? null : (Bundle[]) result.toArray(new org.osgi.framework.Bundle[result.size()]);
     }

     void addRequirers(BundleDescription dependent, ArrayList result) {
         if (dependent.getHost() != null) // don't look in fragments.
 return;
         BundleLoaderProxy dependentProxy = getBundleLoader().getLoaderProxy(dependent);
         if (dependentProxy == null)
             return; // bundle must have been uninstalled
 if (result.contains(dependentProxy.bundle))
             return; // prevent endless recusion
 BundleLoader dependentLoader = dependentProxy.getBundleLoader();
         BundleLoaderProxy[] requiredBundles = dependentLoader.requiredBundles;
         int[] reexportTable = dependentLoader.reexportTable;
         if (requiredBundles == null)
             return;
         int size = reexportTable == null ? 0 : reexportTable.length;
         int reexportIndex = 0;
         for (int i = 0; i < requiredBundles.length; i++) {
             if (requiredBundles[i] == this) {
                 result.add(dependentProxy.bundle);
                 if (reexportIndex < size && reexportTable[reexportIndex] == i) {
                     reexportIndex++;
                     BundleDescription[] dependents = dependent.getDependents();
                     if (dependents == null)
                         return;
                     for (int j = 0; j < dependents.length; j++)
                         dependentProxy.addRequirers(dependents[j], result);
                 }
                 return;
             }
         }
         return;
     }

     public String getSymbolicName() {
         return description.getSymbolicName();
     }

     public Version getVersion() {
         return description.getVersion();
     }

     public boolean isRemovalPending() {
         return description.isRemovalPending();
     }

     BundleDescription getBundleDescription() {
         return description;
     }

     PackageSource getPackageSource(String pkgName) {
         // getByKey is called outside of a synch block because we really do not
 // care too much of duplicates getting created. Only the first one will
 // successfully get stored into pkgSources
 PackageSource pkgSource = (PackageSource) pkgSources.getByKey(pkgName);
         if (pkgSource == null) {
             pkgSource = new SingleSourcePackage(pkgName, -1, this);
             synchronized (pkgSources) {
                 pkgSources.add(pkgSource);
             }
         }
         return pkgSource;
     }

     boolean inUse() {
         return description.getDependents().length > 0;
     }

     boolean forceSourceCreation(ExportPackageDescription export) {
         if (!export.isRoot())
             return true;
         boolean strict = Constants.STRICT_MODE.equals(bundle.framework.adaptor.getState().getPlatformProperties()[0].get(Constants.OSGI_RESOLVER_MODE));
         return (export.getDirective(Constants.INCLUDE_DIRECTIVE) != null) || (export.getDirective(Constants.EXCLUDE_DIRECTIVE) != null) || (strict && export.getDirective(Constants.FRIENDS_DIRECTIVE) != null);
     }
     // creates a PackageSource from an ExportPackageDescription. This is called when initializing
 // a BundleLoader to ensure that the proper PackageSource gets created and used for
 // filtered and reexport packages. The storeSource flag is used by initialize to indicate
 // that the source for special case package sources (filtered or re-exported should be stored
 // in the cache. if this flag is set then a normal SinglePackageSource will not be created
 // (i.e. it will be created lazily)
 PackageSource createPackageSource(ExportPackageDescription export, boolean storeSource) {
         PackageSource pkgSource = null;
         // check to see if it is a reexport
 if (!export.isRoot()) {
             pkgSource = new ReexportPackageSource(export.getName());
         } else {
             // check to see if it is a filtered export
 String includes = (String ) export.getDirective(Constants.INCLUDE_DIRECTIVE);
             String excludes = (String ) export.getDirective(Constants.EXCLUDE_DIRECTIVE);
             String [] friends = (String []) export.getDirective(Constants.FRIENDS_DIRECTIVE);
             if (friends != null) {
                 boolean strict = Constants.STRICT_MODE.equals(bundle.framework.adaptor.getState().getPlatformProperties()[0].get(Constants.OSGI_RESOLVER_MODE));
                 if (!strict)
                     friends = null; // do not pay attention to friends if not in strict mode
 }
             if (includes != null || excludes != null || friends != null) {
                 ExportPackageDescription[] exports = description.getExportPackages();
                 int index = -1;
                 int first = -1;
                 for (int i = 0; i < exports.length; i++) {
                     if (first == -1 && exports[i].getName().equals(export.getName()))
                         first = i;
                     if (exports[i] == export && first != i) {
                         index = i;
                         break;
                     }
                 }
                 pkgSource = new FilteredSourcePackage(export.getName(), index, this, includes, excludes, friends);
             }
         }

         if (storeSource) {
             // if the package source is not null then store the source only if it is not already present;
 // getByKey is called outside of a synch block because we really do not
 // care too much of duplicates getting created. Only the first one will
 // successfully get stored into pkgSources
 if (pkgSource != null && pkgSources.getByKey(export.getName()) == null)
                 synchronized (pkgSources) {
                     pkgSources.add(pkgSource);
                 }
         } else {
             // we are not storing the special case sources, but pkgSource == null this means this
 // is a normal package source; get it and return it.
 if (pkgSource == null)
                 pkgSource = getPackageSource(export.getName());
         }

         return pkgSource;
     }

     class ReexportPackageSource extends PackageSource {
         public ReexportPackageSource(String id) {
             super(id);
         }

         public synchronized SingleSourcePackage[] getSuppliers() {
             PackageSource source = getBundleLoader().getPackageSource(id);
             if (source == null)
                 return null;
             return source.getSuppliers();
         }

         public Class loadClass(String name) {
             try {
                 return getBundleLoader().findClass(name, false);
             } catch (ClassNotFoundException e) {
                 return null;
             }
         }

         public URL getResource(String name) {
             return getBundleLoader().findResource(name, false);
         }

         public Enumeration getResources(String name) throws IOException {
             return getBundleLoader().findResources(name);
         }
     }
 }

